#include "editor.h"
#include "Networking/documentmanager.h"
#include "Networking/session.h"
#include "MainWindow/mainwindow.h"

#include <QTextDocumentFragment>
#include <QMessageBox>

const char *Editor::DEFAULT_NAME = "Untitled";

Editor::Editor(DocumentManager *newManager)
{
	revisionNum = 0;
	documentManager = newManager;
	highlighter = new UserHighlighter(document(), newManager->getSession()->getUserManager());

	connect(this, SIGNAL(copyAvailable(bool)), this, SLOT(copyIsAvaliable(bool)));
}

Editor::~Editor()
{
	delete highlighter;
}

/**
  * Hide or restore the current document from view in the MainWindow tab bar.
  */
void Editor::hideOrRestoreDocument(bool checked)
{
	if (checked)
		MainWindow::getInstance()->restoreEditorTab(this);
	else
		MainWindow::getInstance()->hideEditorTab(this);
}

/**
  * Indent the current line or a selection.
  */
void Editor::indent()
{
	// Indent only the document which is visible.
	if (isVisible())
	{
		QTextCursor cursor = textCursor();

		// Indent a selection.
		if (cursor.hasSelection())
		{
			int start = cursor.selectionStart();
			int end = cursor.selectionEnd();
			bool doneStart = false;

			for (int i = start; i <= end; i++)
			{
				if (document()->characterAt(i).toLatin1() == 0)
				{
					cursor.setPosition(i + 1);
					cursor.insertText("\t");
					i++;
				}
			}

			// Hack. If cursor is on a new line, we assume we want that line indented too.
			if (document()->characterAt(start).toLatin1() == 0)
				start--;

			for (int i = start; !doneStart; i--)
			{
				if ((document()->characterAt(i).toLatin1() == 0) || (i < 0))
				{
					cursor.setPosition(i + 1);
					cursor.insertText("\t");

					doneStart = true;
				}
			}
		}

		// Indent the current line.
		else
		{
			cursor.select(QTextCursor::LineUnderCursor);
			cursor.setPosition(cursor.selectionStart());
			cursor.insertText("\t");
		}
	}
}

/**
  * Unindents the current line or a selection.
  */
void Editor::unindent()
{
	// Unindent only the visible document.
	if (isVisible())
	{
		QTextCursor cursor = textCursor();

		// Unindent a selection.
		if (cursor.hasSelection())
		{
			int start = cursor.selectionStart();
			int end = cursor.selectionEnd();
			bool doneStart = false;

			for (int i = start; i <= end; i++)
			{
				if (document()->characterAt(i).toLatin1() == 0
				    && document()->characterAt(i + 1) == '\t')
				{
					cursor.setPosition(i + 1);
					cursor.setPosition(i + 2, QTextCursor::KeepAnchor);
					cursor.removeSelectedText();;
					i++;
				}
			}

			// Hack. If cursor is on a new line, we assume we want that line unindented too.
			if (document()->characterAt(start).toLatin1() == 0)
				start--;

			for (int i = start; !doneStart; i--)
			{
				if ((document()->characterAt(i).toLatin1() == 0) || (i < 0))
				{
					if (document()->characterAt(i + 1) == '\t')
					{
						cursor.setPosition(i + 1);
						cursor.setPosition(i + 2, QTextCursor::KeepAnchor);
						cursor.removeSelectedText();;
					}

					doneStart = true;
				}
			}
		}

		// Unindent current line.
		else
		{
			cursor.select(QTextCursor::LineUnderCursor);
			cursor.setPosition(cursor.selectionStart());

			if (document()->characterAt(cursor.position()) == '\t')
			{
				cursor.setPosition(cursor.position() + 1, QTextCursor::KeepAnchor);
				cursor.removeSelectedText();
			}
		}
	}
}

void Editor::selectAll()
{
	if (isVisible())
		QTextEdit::selectAll();
}

void Editor::selectNone()
{
	if (isVisible())
	{
		QTextCursor newCursor = textCursor();
		newCursor.clearSelection();
		setTextCursor(newCursor);
	}
}

void Editor::copy()
{
	if (isVisible())
		QTextEdit::copy();
}

void Editor::cut()
{
	if (isVisible())
		QTextEdit::cut();
}

void Editor::paste()
{
	if (isVisible())
		QTextEdit::paste();
}

void Editor::deleteSelection()
{
	if (isVisible())
		textCursor().removeSelectedText();
}

void Editor::undo()
{
	if (isVisible())
		QTextEdit::undo();
}

void Editor::redo()
{
	if (isVisible())
		QTextEdit::redo();
}

void Editor::copyIsAvaliable(bool available)
{
	if (isVisible())
		MainWindow::getInstance()->toggleCutCopyButtons(available);
}
void Editor::saveUserStats(QString filename)
{
    //Save these out to a file
	QByteArray myBytes;	    //Byte array on user statistics
	QFile file(filename);
	file.open(QIODevice::WriteOnly);

	this->saveUserStats(myBytes);	//Get byte array of data to be written
	file.write(myBytes);

	file.close();   //Close the file, done
}

/**
  * Save user statistics to the given filename.
  */
void Editor::saveUserStats(QByteArray& myBytes)
{
    //Save out all common statistical information

	//Parse the document to work out optimal size for the array
	int size = 0;       //Size of the user statistcs array
        int currentUser = -1;   //User associated with the current character of text
        int nextUser = -1;      //User associated with the next character of text

        QTextCursor cursor = textCursor();
        cursor.setPosition(0, QTextCursor::MoveAnchor);     //Move cursor to beginning of document

        currentUser = cursor.charFormat().property(QTextFormat::UserProperty).toInt();  //Grab user property, eg user ID
	size++;

        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, 1);    //Move cursor to next character

	while(!cursor.atEnd())      //While not at the end of the document
	{
            nextUser = cursor.charFormat().property(QTextFormat::UserProperty).toInt(); //Check the next character's user property
	    if(nextUser!=currentUser)   //If it is a new user make a space in the yet declared array
		size++;
            currentUser=nextUser;       //Update variables
	    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, 1);    //Next char, begin process again
	}

	//Create array for user_property
        size += 5;
	int* currentUserID = new int[size];     //Create array with 10 extra space just in case
	int* lenText = new int[size];           //Create corresponding array to hold the number of consecutative character typed by same user
        int sharedPos = 0;                      //Index of the array

        //Fill array with invalid values
	for(int x=0; x<size; x++)
	{
            currentUserID[x]=-1;
            lenText[x]=-1;
	}

	//Grab all of the current user data to get colour and name information
	cursor.setPosition(0, QTextCursor::MoveAnchor);     //Move cursor to beginning of document

	//Set first value
        currentUserID[sharedPos]=cursor.charFormat().property(QTextFormat::UserProperty).toInt();  //Grab user property, eg user ID

	while(!cursor.atEnd())      //While not at the end of the document
	{
            cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 1);    //Move cursor to next character
            nextUser = cursor.charFormat().property(QTextFormat::UserProperty).toInt(); //Check the next character's user property

	    if(nextUser!=currentUserID[sharedPos])  //Another users characters found
	    {
                lenText[sharedPos] = cursor.selectionEnd()-cursor.selectionStart();
		sharedPos++;                        //Move to next position in array
		currentUserID[sharedPos]=nextUser;  //Add user ID
                cursor.clearSelection();
            }
	}
        lenText[sharedPos] = cursor.selectionEnd()-cursor.selectionStart();

        //Fix length method error
        lenText[0]-=1;
        lenText[sharedPos]+=1;

    //Save these out to a file

	//Create a table of value pairs eg. ID numChars
	QString line = "";
	QString data = "";
	sharedPos=0;

	for(int x=0; x<size; x++)       //Write out information to file
	{
	    line = QString::number(currentUserID[sharedPos]) + " " + QString::number(lenText[sharedPos]) + "\n";
	    data += line;
	    sharedPos++;
	}

	myBytes = data.toLatin1();

	delete [] currentUserID;
	delete [] lenText;
}

/**
  * Saves the current session to the given filename.
  */
QString Editor::saveSession(QString filename)
{
	//Save the file
	this->saveFile(filename+"_text.wed");

	//Save the user statistics
	this->saveUserStats(filename+"_uStats.wed");

	//Save other type specific information
	    //int docId, int revisionNum; QString tabName;
	QString line = "";
	line = QString::number(docId) + ":" + QString::number(revisionNum) + ":" + tabName;

	return line;
}

QString Editor::getMeta()
{
	//Save other type specific information
	    //int docId, int revisionNum; QString tabName;
	QString line = "";
	line = QString::number(docId) + ":" + QString::number(revisionNum) + ":" + tabName;

	return line;
}

void Editor::loadSession(QString filename, QStringList metadata)
{
	//Load the file
        this->loadFile(filename+"_text.wed");

        //Load the user statistics for file
        this->loadUserStats(filename+"_uStats.wed");

        //Set other information
	this->setMeta(metadata);
}

void Editor::loadUserStats(QString filename)
{
    //Open the file
    QByteArray myBytes;
    QFile userData(filename);
    if(!userData.open(QIODevice::ReadOnly | QIODevice::Text))
    {
	QMessageBox msgBox;
	msgBox.setText("Error: Unable to load user statistics information for " + filename);
	msgBox.exec();
	return;
    }

    myBytes = userData.readAll();

    this->loadUserStats(myBytes);	//Load in the information for the current document

    userData.close();
}

void Editor::loadUserStats(QByteArray myBytes)
{
    //Read in the statistics information and apply to the document
    QTextStream in(&myBytes);
    int userN = -1;
    int chars = -1;

    in >> userN >> chars;   //Read in first lot of values

    bool previousSignalState = document()->blockSignals(true);

    QTextCursor cursor = textCursor();                  //Create cursor
    cursor.setPosition(0, QTextCursor::MoveAnchor);     //Move cursor to beginning of document
    QTextCharFormat myFmt;

    while(userN!=-1 && chars !=-1)  //While havn't reached the end
    {
	document()->blockSignals(true);
	myFmt.setProperty(QTextFormat::UserProperty, QVariant(userN));
	cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, chars);    //Move cursor to next character
	cursor.mergeCharFormat(myFmt);
	cursor.clearSelection();

	in >> userN >> chars;       //Read in next values
    }

    document()->blockSignals(previousSignalState);
}

void Editor::setMeta(QStringList metadata)
{
    //Set other information
    docId = QString(metadata.at(0).toLocal8Bit().constData()).toInt();
    revisionNum = QString(metadata.at(1).toLocal8Bit().constData()).toInt();
    tabName = metadata.at(2).toLocal8Bit().constData();
}
